Git Product home page Git Product logo

xcstrings-tool's Issues

Support deferred formatting

If we take the following example:

let date = Date()
var resource: LocalizedStringResource = .localizable.todaysDate(date.formatted(.dateTime))
resource.locale = Locale(identifier: "en_US")

You might at a glance expect the date to be formatted using en_US formatting rules, but it is not. This is because date.formatted(date: .numeric, time: .omitted) is resolving a String using Locale.current prior to passing it into the LocalizedStringResource.

Without using XCStrings Tool, if we wanted to format this using the resource (or SwiftUI environment) locale, we'd have to do the following:

var resource: LocalizedStringResource = "Today's date is \(date, format: .dateTime)"
resource.locale = Locale(identifier: "en_US")

\(date, format: .dateTime) is subtly different to date.formatted(.dateTime) because it uses a custom interpolation method:

https://developer.apple.com/documentation/swift/string/localizationvalue/stringinterpolation/appendinterpolation(_:format:)-5arer

It will hold the original value inside the resource alongside the FromatStyle and will only resolve it when required, using the locale information provided.

Because XCStrings Tool only generates methods with a String argument, we cannot benefit from this deferred formatting, which is unfortunate. It would be nice if we could.

Strings Catalogs migrated from .stringsdict don't work

Hey @liamnichols !

I downloaded your demo project DogTracker to test things out, and replaced the Localizable.xcstrings with our own (we have a lot of languages, and a lot of strings - the file itself has 12MB and ~600 000 lines).

When compiling the dog Tracker, error is shown:
Decoding error at ‘strings → common.feed.translations_seen_by → localizations → ar → substitutions → number_of_parents‘ - The data couldn’t be read because it is missing.

As you can see on the screenshot, everything looks correct. I wonder if you are able to debug this issue. Note: It is using 0.1.1 version released 30 mins ago. :)

Screenshot 2023-11-28 at 13 35 36 Screenshot 2023-11-28 at 13 35 50

Idea: use `.` as a separator to allow nesting

This is just an idea.

For organization, it would be nice to be able to write something like

Text(.localizable.settings.title)

I was wondering if this could not somehow be implemented by splitting the keys found in .xcstrings on the . character, and then generating nested static structs.

Thoughts?

Better error handling for unknown errors

Currently the generator just throws stuff from JSONDecoder and Data etc, which isn't picked up nicely in the issue navigator.

I think we want to wrap run() and format errors consistently (or eliminate errors 😄)

Produce warnings when a Strings Catalog phrases are ignored by the tool

There are two scenarios where we ignore strings in a catalog:

  1. A value is missing in the source language
    • This is common if the key is the phrase
  2. The key is automatically managed
    • This happens when the compiler is syncing the key from Swift source code

We have to ignore these keys because it means that they typically haven't been properly defined by the developer can't cant be represented in Swift, but ignoring them with no warning can leave the developer confused and frustrated when the Swift property cannot be found.

I've even confused myself about this a couple of time during development.

Because XCStrings Tool has strong opinions on using manually created strings with a key and default value, we should produce a warning that can help surface this misconfiguration and guide the developer to correct it.

Duplicates in Localizable.xcstrings in 0.2.0

Hi!

After updating to 0.2.0 of xcstrings-tool-plugin (I use plugin in swift packages inside Xcode project), I've noticed that after build my Localizable.xcstrings were modified by additional strings which duplicates Default Localization values.

I should mention that code base does not have those strings. Catalogs are the source of truth with manual management and used by .localizable.someKey syntax

Screenshot 2024-05-07 at 18 37 51

I found out the exact commit with broken changes 15acd61

So far I fixed the previous commit revision in my project and there is no such issue.

My Xcode version 15.3 and // swift-tools-version: 5.10 in SPM packages.

Better support for use in open source libraries

Currently, we direct you to use XCStrings Tool as a Build Tool plugin, which requires the plugin to be invoked whenever the target is compiled (if the catalog is modified).

This is great for direct use in your own projects as it keeps the generated source up-to-date automatically however it became clear that this is a bit problematic when used internally in an open source library that is offered to other developers because it requires them to trust the plugin before it runs yet the plugin itself is an implementation detail and that might confuse the developer who is adopting the open source library.

To overcome this, we can offer ways to generate the source code on-demand so that it can be bundled in the library release instead of being generated at compile time.

This can be made possible in two ways:

  1. Document the CLI so that it can be invoked manually
  2. Add a Command Plugin that replicates the Build Tool plugin by detecting the Strings Catalogs automatically

The first approach is just a case of adding documentation, but the second is a much friendlier user experience

Suggestion/Question init LocalizedStringKey with .localizable.key

Hi, great tool, i anticipated the need for localization and used LocalizedStringKey wherever it made sense in my project.
Right now i'm utilizing your tool generated code as follows "\(.localizable.localizedStringKey)", it makes perfect sense to be able to escape the String interpolation completely and just have your library extend the LocalizedStringKey.
Or am i missing something and there's already a way?

Add file annotations to errors

We have errors surfaced in the issue navigator which is great, but they aren't linked to the .xcstrings file so clicking them takes you to the build log.

While we can't do line number navigation, it might be better if we take you to the file still?

Duplicate accessors generated in extensions on both `String` and `LocalizedStringResource`

The first version of generated code was a series of static computed properties and functions on a type nested under LocalizedStringResource. It allowed us to use dot syntax to access the resources in a nice way:

Text(.localizable.myKey)
// or
String(localized: .localizable.myKey)

This was great, but since it relied on LocalizedStringResource, it relied on iOS 16+, which turned out to not be great. To improve backwards compatibility, we also added an extended type under String, and generate an initialiser so that you can do this:

String(localizable: .myKey) // note 'localizable' is the associated table name

Under the hood, we also generated LocalizedStringResource.init(localizable:), but it was kept private since we were regenerating the myKey property.

Now that I'm focusing on #60, and seeing that locking into LocalizedStringResource was a bit premature, i'm considering if it is actually worth generating all of the accessors on both String.Localizable and LocalizedStringResource.Localizable.

Instead, i'm considering to deprecate LocalizedStringResource.Localizable and instead generate the following global function:

func localizable(_ localizable: String.Localizable) -> LocalizedStringResource {
    LocalizedStringResource(localizable: localizable)
}

The aim for this is to try and maintain that nice syntax we have for accessing the constants in a nice way. For example:

- Text(.localizable.myKey)
+ Text(localizable(.myKey))

Once #60 is complete, this would really shine, because it would allow us to generate another global function that can return a LocalizedStringKey type which can be used to then obtain a LocalizedStringKey representation of String.Localizable and use it in the number of places of the SwiftUI API that don't accept LocalizedStringResource.

I'm opening an issue to track this because it would be a breaking change to the generated code so I would like to understand peoples opinions on it. I'd do it in stages across three releases:

  1. Introduce the global functions and mark the LocalizedStringResource.Localizable types as deprecated without producing a warning
  2. Update the deprecations to omit a warning
  3. Remove the LocalizedStringResource.Localizable type from the generated code

Keys which are Swift keywords can't be compiled

If you have a localized string with a key named continue, repeat, or any other keywords, the generated Swift code just inserts them as is, and so the files don't compile. A workaround is to name these something else, such as continueLabel, but this could also be solved by escaping any keys which are known Swift keywords by wrapping them in backticks

How to use with Github Actions?

I am using Github Actions to build my app and distribute it to Testflight.

- name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

- name: build archive
        run: |
          xcodebuild -scheme "AppName" \
          -archivePath $RUNNER_TEMP/AppName.xcarchive \
          -sdk iphoneos \
          -configuration Release \
          -destination generic/platform=iOS \
          clean archive          

- name: export ipa
  env:
    EXPORT_OPTIONS_PLIST: ${{ secrets.EXPORT_OPTIONS_PLIST }}
  run: |
    EXPORT_OPTS_PATH=$RUNNER_TEMP/ExportOptions.plist
    echo -n "$EXPORT_OPTIONS_PLIST" | base64 --decode -o $EXPORT_OPTS_PATH
    xcodebuild -exportArchive -archivePath $RUNNER_TEMP/AppName.xcarchive -exportOptionsPlist $EXPORT_OPTS_PATH -exportPath $RUNNER_TEMP/build

Would this plugin work with my automation?
Or what would I need to do to make it work?

ExtractionState config

Right now the code checks for extractionState == .manual and produces result only in this case.
This is probably correct thing to do by default. However in our situation, we have strings stored in online tool (so it can be pulled for both iOS/Android projects), it's a source of truth for us. And after downloading xscstrings catalog, extractionState is missing (and will be set to stale after build), so nothing will be generated.

It'll be nice to have some way of extractionState check configuration from outside, thanks!

[Refactor] Use CodingKeyRepresentable

https://developer.apple.com/documentation/swift/codingkeyrepresentable

For the string catalog decodable implementation to replace the DictionaryWrapper type.

We use this type instead of a dictionary directly, because dictionaries with RawRepresentable keys do not decode properly by default.

I think making these RawRepresentable types conform to the CodingKeyRepresentable protocol will fix that and allow us to use Dictionary directly.

I have not confirmed this though

Include availability headers

The code can theoretically generate into projects that support < iOS 16, so we should include the appropriate availability headers.

Sendable conformance

Thanks for open sourcing this!

Using it on some parts marked @MainActor. Xcode produces the following warnings:

Passing argument of non-sendable type 'LocalizedStringResource' outside of main actor-isolated context may introduce data races

Being static strings I think we should be able to make it @sendable? 🤔

StringCatalog Edge Cases

It seems like the nullability requirements are too strict in the current implementation.

I'm seeing entirely empty objects representing some strings so it seems like a key alone is apparently enough.

String containing a single `%` is parsed incorrectly

Given the following:

Screenshot 2024-05-06 at 10 27 18

The current code generates as so:

/// A fixed string containing the % symbol
public static func percentageDiscount(_ arg1: UInt) -> Self {
    Self (
        key: "percentageDiscount",
        defaultValue: ###"50\###(arg1)ff"###,
        table: "Localizable",
        locale: .current,
        bundle: .current
    )
}

But this is wrong, it should be:

public static var percentageDiscount: Self {
    Self (
        key: "percentageDiscount",
        defaultValue: ###"50% off"###,
        table: "Localizable",
        locale: .current,
        bundle: .current
    )
}

Add public for package

When I setup XCSTRINGS_TOOL_ACCESS_LEVEL_PUBLIC for my LocalizationKit package. I can't get access for generated extension.

How fix

func generateLocalizedStringResourceExtension() -> ExtensionDeclSyntax {
        ExtensionDeclSyntax(
            attributes: [
                .attribute(.init(availability: .wwdc2022))
                    .with(\.trailingTrivia, .newline)
            ],
            modifiers: [
                DeclModifierSyntax(name: .keyword(.public)) <======= Need add for access from other package 
            ],
            extendedType: IdentifierTypeSyntax(name: "LocalizedStringResource"),

Default values with UnsafeRawPointers do not compile

Hi there,

First of all, I'm really glad you've created this, even though I'm scared to add any new dependencies to the project because Swift 6 is close, I'll give it a try 😅

I found an issue with %s and %p parameters.
I wanted to fix it by myself and open a PR, but it's the first time I've seen ### formatting, so I guess you know what to do better and faster :)

image

Btw you might want to create 0.1.3 anyway, as some handy fixes to the documentation were added since 0.1.2 🖖🏻

[Contributing] Document how to test changes

The majority of tests for the tool are implemented using snapshot tests, but its not clear to new contributors how to go about utilising them (ref: #22 (comment))

It would be good to include some instructions in CONTRIBUTING.md to help make it easier for people.

Generate.swift in the Release files look different

Hey Liam👋🏽

I'm now trying to use the xcstrings-tool for my Swift Package target but I faced some errors so let me ask here.

When I add the plugin and try to build it I get these errors ↓

Screenshot 2024-04-25 at 21 07 52

It seems it's losing : URL type annotations for input and output in the Generate.swift when it's packed into the release zip files. I've checked some latest releases but none of them have this URL correctly.

Do you have any idea why this happens? Also, let me know if I can be of help :)

Screenshot 2024-04-25 at 21 08 05

Mirror plugin in xcstrings-tool-plugin repo

Currently the plugin is defined as part of this repo, which means that dependant projects must bring in the entire xcstrings-tool repo and it's dependencies into their build graph.

This adds compilation time and i'm wondering if it could be avoided.

We could publish a binary artefact of the xcstrings-tool cli and instead host the plugin in a dedicated repo. This would give a few benefits:

  1. We can lower the deployment target as the plugin doesn't use ResultBuilder or other macOS 13+ APIs
  2. Using a binary dependency will significantly reduce compile times for other projects

But it also has some cons since it is another repo to maintain. I think we can automate most of this though to eliminate those issues.

Document steps for deintegrating XCStrings Tool

I don't want anybody to feel locked-into a third party tool, and they're not. But because the source code isn't copied into the repo, it's not clear that you can obtain it.

Add an article with explicit instructions on how to copy the generated source out and deintegrate the tool if you wish.

Food Truck Demo

Demo migrating Apple's Food Truck app.
A simplified version would also make a good tutorial.

[Contributing] Document the release process

Adding a note here for now because I forgot...

  1. Bump the version (example)
  2. Download xcstrings-tool.artifactbundle.zip from the workflow run for the version bump commit (example)
    • Make sure that it's not double zipped (unzip -l)
  3. Compute it's checksum with shasum -a 256 <path>
  4. Create a new release and include the artifactbundle and it's sha (see other releases as an example)
  5. Update https://github.com/liamnichols/xcstrings-tool-plugin
    • Update the binaryTarget in Package.swift for the new version
    • Copy the plugin over if it was modified
  6. Create a new release in the plugin repository
  7. Update https://github.com/liamnichols/xcstrings-tool-demo

Set default value to be fallback documenting comment

When option+click on a string, it would nice to be able to see the default value of the string for quick reference.
When a comment is set in the string catalog, this is the documenting comment, but I think setting the defaultValue as the fallback would be massively helpful, as it is in swiftgen, which is what I'm trying to move away from.

Thanks

Ensure that snapshots compile

It was noted in #55 that there are some compiler errors with some of the snapshots that went unnoticed (due to more edge-case configurations).

It would be beneficial to ensure that the snapshots compile as part of the CI checks to catch this kind of thing earlier on.

Compiling a single file for macOS should be a relatively straightforward swift cli invocation. We might even be able to call it from the test suite itself rather than some other kind of script.

Support for reading legacy strings files and providing fallback strings

First off, I was about to write a similar plugin before I did a quick search and found yours. Glad I did because this looks great! 👏

I'm looking into replacing the use of SwiftGen in a project I'm working on with this plugin but there are two features I'd need before being able to do so. Before contributing I'd like to gauge interest to see whether you think the features are suitable additions to your plugin. These are:

  • The ability to parse legacy string files (for those who haven't migrated to string catalogs).
  • The ability to fall back to a base localization if translation isn't complete. So for example, going with the English string if the German counterpart is missing.

What do you think? To be clear, if these features are something you think would be valuable additions I'm more than happy to contribute.

Support macOS 12/iOS 15/etc by using String(localized:) instead of LocalizedStringResource

Currently the plugin generates the static let properties as a LocalizedStringResource, but String has an initializer that works with String Catalogs as well:

public init(localized key: StaticString, defaultValue: String.LocalizationValue, table: String? = nil, bundle: Bundle? = nil, locale: Locale = .current, comment: StaticString? = nil)

Using String rather than LocalizedStringResource will allow this library to support macOS 12/iOS 15/etc.

If you're working with features that utilize XPC and need locale lookups to happen on the fly, this would break that functionality. But I can't imagine that's very common currently.

Permission error in Xcode Cloud

Hi,

Thank you for creating xcstrings-tool.
It works fine on my macbook but not on Xcode Cloud.

スクリーンショット 2024-01-03 17 47 38

( Compose is the name of my Swift package)

I am very new to build tools plugin mechanism. Do you have any ideas?

Decoding error with substitutions

In my app I have some scenarios where I need to pluralise a string that needs to display not a number directly but a formatted value for it. I'm using substitutions for this, which seems to work just fine in Xcode by itself, but these don't seem to be parsed correctly by XCStrings-tool, and I get an error reading:

Decoding error at ‘strings → Days %lf %@ → localizations → en → substitutions → count‘ - The data couldn’t be read because it is missing.

Here's the raw source for one of the keys that's causing this issue:

"Steps %lf %@" : {
   "localizations" : {
      "en" : {
         "stringUnit" : {
            "state" : "translated",
            "value" : "%#@count@"
         },
         "substitutions" : {
            "count" : {
               "formatSpecifier" : "lf",
               "variations" : {
                  "plural" : {
                     "one" : {
                        "stringUnit" : {
                           "state" : "translated",
                           "value" : "%2$@ step"
                        }
                     },
                     "other" : {
                        "stringUnit" : {
                           "state" : "translated",
                           "value" : "%2$@ steps"
                        }
                     }
                  }
               }
            }
         }
      }
   }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.