Convert Laravel PHP array to CSV

CSV is what translators and agencies actually work with: one key per row, one value per column, editable in any spreadsheet.

Why translators want a spreadsheet

Professional translators do not want your repository. They want a two-column grid where the left side is a stable identifier they must not touch and the right side is text they can type into. CSV is the lowest common denominator that every translator, agency and freelancer on earth can already open, and handing one over removes the entire class of problems that begins with "I edited the PHP file and now the site is down".

The conversion flattens your nested array into one row per string, using dot notation to encode the hierarchy. That flattening is what makes the file editable in a spreadsheet, and it is fully reversible: the CSV to PHP converter rebuilds the original nesting from the same dotted keys.

Input — lang/en/messages.php

<?php

return [
    'welcome' => 'Welcome back, :name!',
    'cart' => [
        'empty' => 'Your cart is empty',
        'items' => ':count item|:count items',
    ],
];

Output — messages.csv

key,value
welcome,"Welcome back, :name!"
cart.empty,"Your cart is empty"
cart.items,":count item|:count items"

Values containing commas, quotes or line breaks are quoted according to RFC 4180, so the file survives a round trip through Excel, Google Sheets, LibreOffice and every CAT tool that accepts delimited text.

The spreadsheet problems nobody warns you about

CSV is a simple format wrapped in a minefield of application-specific behaviour. Most corrupted translation files are corrupted by the spreadsheet, not by the converter, and the damage is usually invisible until the strings reach production.

Excel and the missing accents. Excel on Windows does not assume UTF-8 when you double-click a .csv. It applies the system codepage, so Café becomes Café and every non-Latin script becomes garbage. The fix is to import rather than open: use Data → From Text/CSV and explicitly select UTF-8. Google Sheets and LibreOffice get this right by default.

The semicolon problem. In locales where the decimal separator is a comma — most of continental Europe — Excel writes CSV using semicolons as the delimiter. A translator in Germany can open your comma-delimited file, save it, and hand back something your parser reads as a single column. Agreeing on the delimiter up front avoids a frustrating round trip.

Formula injection. Any value beginning with =, +, - or @ is interpreted as a formula. A perfectly ordinary string like -50% off today becomes an error cell, and the translator returns #NAME? instead of your discount banner. This also has a security dimension when the CSV is opened by someone else, since a crafted formula can execute on their machine.

Autocorrect and type coercion. Spreadsheets are eager to be helpful. A key like 1.5 becomes a number, a value like 03/04 becomes a date, long digit strings lose precision, and straight quotes silently become curly ones. Curly quotes are the nastiest of these because the string still looks correct to a reviewer.

Ask translators to format the entire sheet as Text before typing, and to paste as plain text. It takes them ten seconds and eliminates the majority of these failures.

Designing the handoff so the file comes back clean

The two-column output is the minimum. In practice a translation handoff works far better with a few extra columns added before you send it, because it turns a bare list of strings into a brief.

Column Filled by Purpose
key You (locked) The identifier. Must survive untouched.
source You (locked) The English original, for reference.
target Translator The only editable column.
context You Where the string appears; who says it.
max_length You Character budget for buttons and labels.
notes Translator Questions and flags, instead of email threads.

Context is the highest-value column by a wide margin. A key like cart.empty is unambiguous, but thousands of real keys are words like post, close or order, which are verbs or nouns depending on the screen and translate completely differently in each case. Without context a translator guesses, and roughly half the guesses are wrong.

The max_length column matters because text expands. German and Finnish routinely run thirty to forty per cent longer than English, and a button that fits "Save" will not fit its German equivalent. Telling the translator the budget in advance is much cheaper than discovering the overflow in QA.

When to reach for this conversion

  • Commissioning an external translation. The agency gets a spreadsheet, you get a spreadsheet back, and nobody touches the codebase.
  • Bulk review by a native speaker. A colleague who is not a developer can read four hundred strings in a grid far faster than in a PHP file.
  • Auditing coverage. Export each locale, put them side by side in one sheet, and empty cells show you exactly what is missing.
  • Copy rewrites. When marketing wants to overhaul tone across the whole product, a spreadsheet is the only interface they will actually use.
  • Feeding a CAT tool. Trados, memoQ and Smartcat all ingest delimited text and can apply translation memory to it.

It is worth being honest about the limits. A CSV handoff is a snapshot, and the moment you ship a new feature the spreadsheet is stale. Teams that translate continuously rather than in batches eventually outgrow this loop — not because CSV is bad, but because reconciling five returned spreadsheets against a codebase that moved on two weeks ago is nobody's idea of a good afternoon.

Preparing the export itself

A few minutes of preparation before you send the file prevents most of the reconciliation pain afterwards. The goal is that the spreadsheet you receive can be converted back without a human reading every row.

  1. 1 Export one file at a time. Convert messages.php and validation.php separately rather than merging them. Laravel namespaces short keys per file, so merging loses the boundary and two title keys will collide.
  2. 2 Strip what should not be translated. Framework strings from Laravel-Lang are already professionally translated into most locales. Paying an agency to redo validation.php is wasted money — send only the strings your team actually wrote.
  3. 3 Freeze the keys. Lock or protect the key column in the sheet before sending. A single sorted-by-value operation that moves keys out of alignment with their values will silently scramble the entire file.
  4. 4 Include the plural strings deliberately. A value like :count item|:count items means nothing to a translator who has not been told about the pipe convention, and target languages may need three or four forms rather than two. Flag those rows and explain the syntax.
  5. 5 Send a completed example row. One filled-in row communicates the expected format better than a page of instructions.

Keep the original export. When the translated file returns, a diff of the key columns immediately reveals rows that were added, deleted or reordered — the three things that break an automated re-import and the three things that are almost impossible to spot by eye.

Frequently asked questions

Does the converter keep :placeholders and plural pipes intact?

Yes. Values are treated as opaque strings, so Laravel placeholders like :name or :count and pipe-separated plural forms come through unchanged.

How is nesting handled?

Nested keys are flattened to dot notation in CSV (cart.empty) and re-expanded into nested structures when converting back.

Is my data uploaded or stored?

The conversion runs on our server but nothing is persisted: files are converted in memory and the response is returned immediately. For PHP input, only string, number and array literals are parsed — code is never executed.

Is there a size limit?

Yes, 200 KB per conversion, which covers even very large lang files. If you need bulk conversion across many locales, import your files into LangSyncer and export any format.

Converting files by hand gets old fast

Import your lang files into LangSyncer once, edit and translate them in a dashboard, and export any format — or skip files entirely with live translations.

We use cookies to improve your experience and analyze site traffic. Cookie Policy

Cookie Preferences

Essential

Required for the site to work

Analytics

Help us improve the site

Marketing

Personalized ads and content