Giter VIP home page Giter VIP logo

friendly_id-globalize's Introduction

FriendlyId Globalize

Globalize support for FriendlyId.

Installation

gem 'friendly_id-globalize'
rails generate friendly_id_globalize

Translating Slugs Using Globalize

The FriendlyId::Globalize Globalize module lets you use Globalize to translate slugs. This module is most suitable for applications that need to be localized to many languages. If your application only needs to be localized to one or two languages, you may wish to consider the FriendlyId::SimpleI18n SimpleI18n module.

In order to use this module, your model's table and translation table must both have a slug column, and your model must set the slug field as translatable with Globalize:

class Post < ActiveRecord::Base
  translates :title, :slug
  extend FriendlyId
  friendly_id :title, :use => :globalize
end

Note that call to translates must be made before calling friendly_id.

Finds

Finds will take the current locale into consideration:

I18n.locale = :it
Post.find("guerre-stellari")
I18n.locale = :en
Post.find("star-wars")

Additionally, finds will fall back to the default locale:

I18n.locale = :it
Post.find("star-wars")

To find a slug by an explicit locale, perform the find inside a block passed to I18n's with_locale method:

I18n.with_locale(:it) { Post.find("guerre-stellari") }

Creating Records

When new records are created, the slug is generated for the current locale only.

Translating Slugs

To translate an existing record's friendly_id, use FriendlyId::Globalize::Model#set_friendly_id. This will ensure that the slug you add is properly escaped, transliterated and sequenced:

post = Post.create :name => "Star Wars"
post.set_friendly_id("Guerre stellari", :it)

If you don't pass in a locale argument, FriendlyId::Globalize will just use the current locale:

I18n.with_locale(:it) { post.set_friendly_id("Guerre stellari") }

friendly_id-globalize's People

Contributors

alepore avatar dimidev avatar kaspernj avatar kevin-jj avatar krisdigital avatar laurynas avatar lenart avatar norman avatar parndt avatar sasselin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

friendly_id-globalize's Issues

Redirect same page to different language (link_to)

Hi there,

I've been scratching my head for the last few hours, looking for an answer but I can't find it anywhere.

My gem file:

# Use globalize for translating models
gem "globalize", github: "ncri/globalize" # for Rails 4.2
gem 'globalize-accessors', '~> 0.1.5'

# Use friendly_id for slugs
gem 'friendly_id', '~> 5.1.0'
gem 'friendly_id-globalize', '~> 1.0.0.alpha1'

Here's the situation:

  • I have two languages "en" and "fr"
  • 2 models : pages and pages_translations
  • pages has a slug column, pages_translations also has a slug column.
  • if I view the page -> en/pages/slug-en, it works.
  • if I view the page -> fr/pages-slug-fr, it works.

So I assume friendly_id and globalize are properly configured.

However my problem is that I can't make a language switcher work using:

  <% if I18n.locale != :en %>
    <li>
    <%= link_to t('menu.languages.short_en'), url_for(locale: 'en') %>
    </li>
<% end %>

The route becomes en/pages/slug-fr (i.e. the language changes but not the slug).

I have activated config.use :finders in the initializer.

My page model:

translates :title, :slug, :blurb, :content, :seo_title, :seo_description, :seo_keywords
  globalize_accessors :locales => [:en, :fr], :attributes => [:title, :slug, :blurb, :content, :seo_title, :seo_description, :seo_keywords]

    extend FriendlyId
      friendly_id :slug, :use => :globalize
      validates :slug, presence: true, uniqueness: { case_sensitive: false }

So what do I need to do to have the proper path on my language switcher?
Ideally, I'd like this to work with any models, not just the Page model.

Thanks!

  • Vincent

find() through association isn't using translation table

Using find() directly on my model generates a query which looks up the slug in the page_translations table:

Page.find('my-title')
SELECT FROM "pages" LEFT OUTER JOIN "page_translations" ...
-> #<Page id: 1 ...>

When I use find through a association, the translation table isn't being used. friendly_id uses the orginal table instead.

@site.pages.find('my-title')
SELECT "pages".* FROM "pages" WHERE "pages"."site_id" = $1 AND "pages"."slug" = 'my-title' LIMIT 1  [["site_id", 1]]
-> ActiveRecord::RecordNotFound

In Rails 3.2 (friendly_id 4.0.10, globalize 3.0.0) it works like this:

@site.pages.find('my-title')
SELECT "pages".* FROM "pages" WHERE "pages"."shop_id" = 1 AND "pages"."slug" = 'my-title' LIMIT 1
SELECT DISTINCT "pages".id, pages.position AS alias_0 FROM "pages" LEFT OUTER JOIN "page_translations" ...
SELECT "pages"."id" AS t0_r0, "pages"."title" AS t0_r1 ... FROM "pages" LEFT OUTER JOIN "page_translations"
-> #<Page id: 1 ...>

Rails 5.2 warnings

I am getting a lot of these warnings, after I upgraded a project to Rails 5.2.

DEPRECATION WARNING: Dangerous query method (method whose arguments are used as raw SQL) called with non-attribute argument(s): "\"friendly_id_slugs\".id DESC". Non-attribute arguments will be disallowed in Rails 6.0. This method should not be called with user-provided values, such as request parameters or model attributes. Known-safe values can be passed by wrapping them in Arel.sql(). (called from super_create_slug at /home/dev/.rvm/gems/ruby-2.4.4/bundler/gems/friendly_id-globalize-b5c1b8b442f6/lib/friendly_id/history.rb:130)

Issue with non-translatable models

Gem version: friendly_id-globalize (~> 1.0.0.alpha3)
Ruby version: 2.3.0
Rails version: 5.1

I'm making use of the :history plugin in a bunch of translatable models but also in a couple of non-translatable models like User.

class User < ApplicationRecord
        extend FriendlyId
        friendly_id :full_name, use: [:slugged, :history]
        ...
end

For some reason my User model responds to :translations which causes the friendly_id-globalize to take over the history functionality. I needed to patch a few finder methods to make it work.

Instead of respond_to?(:translations) it is better to use respond_to?(:translation_class).
I also found the need to override the create_slug method to check if the model is making use of Globalize.

You might want to change the exists_by_friendly_id?(id) method too because you are calling "super" on a non existing method. Because the Finder methods are included, they cannot be called with "super" and you must copy the method over from the original gem. You did it for create_slug but forgot to do it for exists_by_friendly_id?(id)

People who are having the same issues can add this monkey patch to their initializers untill the issues are fixed:

module FriendlyId
  module History
    module FinderMethods
      include ::FriendlyId::FinderMethods

      def exists_by_friendly_id?(id)
        if respond_to?(:translation_class)
          joins(:slugs, :translations).where(translation_class.arel_table[friendly_id_config.query_field].eq(id)).exists? || joins(:slugs).where(slug_history_clause(id)).exists?
        else
          joins(:slugs).where(slug_history_clause(id)).exists?
        end
      end

      def slug_history_clause(id)
        if respond_to?(:translation_class)
          Slug.arel_table[:sluggable_type].eq(base_class.to_s).and(Slug.arel_table[:slug].eq(id)).and(Slug.arel_table[:locale].eq(::Globalize.locale))
        else
          Slug.arel_table[:sluggable_type].eq(base_class.to_s).and(Slug.arel_table[:slug].eq(id))
        end
      end
    end

    def create_slug
      if respond_to?(:translation_class)
        translations.map(&:locale).each do |locale|
          ::Globalize.with_locale(locale) {super_create_slug(locale)}
        end
      else
        super_create_slug(nil)
      end
    end
  end
end

disable fallback?

From the doc: Additionally, finds will fall back to the default locale:

Would be easy/reasonable to make this configurable?
For example, for SEO purposes one might not want www.example.com/it/english-slug to be valid, but only www.example.com/it/italian-slug and www.example.com/english-slug.

thoughts?

Generator is not working

I downloaded the gem and installed it, and when trying to run the rails g friendly_id_globalize I get Could not find generator 'friendly_id_globalize'. Maybe you meant 'friendly_id', 'react_on_rails:base' or 'resource_route'

Best way to update slug?

Hi, I use your gem with friendly-id and globalize (last versions) in a Rails 4.2.5 application. I need to update slug every time a user modify the attribute name.

Model :

class Project < ActiveRecord::Base
  # Translations
  TRANSLATED_FIELDS = [:name, :description, :full_description, :slug, :meta_desc, :meta_keys].freeze
  translates *TRANSLATED_FIELDS, :fallbacks_for_empty_translations => true

  # Friendly URL generation
  extend FriendlyId
  friendly_id :name, use: :globalize

  validates_presence_of     :name

end

When I use update action in my controller slug is not regenerated because slug is not nil in translations. I check the code and I realize that the method used to update slug is :

    def should_generate_new_friendly_id?
      translation_for(::Globalize.locale).send(friendly_id_config.slug_column).nil?
    end

How can I manage to set slug attributes to nil?
I try :

  • to do in the form but it's a string i can't pass a nil value
  • to do in the controller in merging attributes with ( { slug: nil} )
  • to use a before validation method in model :
  before_validation :set_slug_nil_if_blank

  def set_slug_nil_if_blank
    self.slug = nil if slug.blank?
  end

I go further and discover that the object instance in should_generate_new_friendly_id? seems to be the old one not the one which is updated.

Is there a classic way to do this?

Boom

If you add a slug index column on the translations table the whole thing blows up.

Unique Slugs by Scope not working

Even after using :scope, friendly_id is generating a hashed version of the slug.

friendly_id (5.1.0)
rails (5.1.4)
ruby (2.1.4

class Board < ApplicationRecord
  extend FriendlyId
  friendly_id :name, use: [:scoped, :slugged, :history], scope: :company

  belongs_to :company
  has_many :posts

  validates :slug, presence: true, uniqueness: { scope: :company }
end
> Board.create name: "Test Board", company: company
=> #<Board:0x007f8e192b53f0
 id: 19,
 company_id: 19,
 name: "Test Board",
 description: nil,
 created_at: Fri, 06 Oct 2017 09:06:26 UTC +00:00,
 updated_at: Fri, 06 Oct 2017 09:06:26 UTC +00:00,
 slug: "test-board">

> Board.create name: "Test Board", company: company
=> #<Board:0x007f8e1bb4e820
 id: 20,
 company_id: 19,
 name: "Test Board",
 description: nil,
 created_at: Fri, 06 Oct 2017 09:06:40 UTC +00:00,
 updated_at: Fri, 06 Oct 2017 09:06:40 UTC +00:00,
 slug: "test-board-1781b893-4c25-4875-acf4-0fb23ca09c52">

Am I missing something? I referred to: http://norman.github.io/friendly_id/file.Guide.html#Unique_Slugs_by_Scope

Bump to new version

@parndt Hi ! Would it be possible to bump this gem (like alpha3) to use last commits and Friendly_id 5.2 ? Thanks !

updating slugs using self.set_friendly_id t.title, t.locale

I'm using slugged together with friendly_id-globalize on my model:

extend FriendlyId
friendly_id :title, :use => [:globalize, :slugged]

The initial generation of the slug works fine, but when I try to update my slugs when the title has changed with:

    self.translations.each do |t|
      t.slug = self.set_friendly_id t.title, t.locale
    end

I get a:

NoMethodError: undefined method `set_friendly_id'

Is there a better, or at least a working way to handle slug-updates?! I've migrated from an earlier version of friendly_id where this approach worked, but as the docs haven't changed on I assumed that this should still be working...

friendly_id-globalize does not override slug parameter

This works with just friendly_id:

class Entry < ActiveRecord::Base
  include FriendlyId
  friendly_id :name, use: :slugged
end
entry = Entry.create! name: 'foo bar', slug: nil
# entry.slug == 'foo-bar'

however with globalize:

class Entry < ActiveRecord::Base
  translates :name, :slug

  include FriendlyId
  friendly_id :name, use: [:slugged, :globalize]
end

entry = Entry.create! name: 'foo bar baz', slug: nil
# entry.slug == nil

only this works:

class Entry < ActiveRecord::Base
  translates :name, :slug

  include FriendlyId
  friendly_id :name, use: [:slugged, :globalize]
end

entry = Entry.create! name: 'foo bar'
# entry.slug == 'foo-bar'

even setting should_generate_new_friendly_id? to always return true doesn't change anything. It doesn't matter if it's a new entry or update on an existing one. As long as the 'slug' parameter exists with any content, it's not being overwritten (should_generate is called, slug does get generated, it's just that it has no effect).

Recreate slugs

Hi there,

Quick support question: how do I recreate my slugs?
I tried Model.find_each(&:save) but it did not work.

everything works perfect on development but not on Heroku

gem file:

gem 'friendly_id'
gem 'globalize'
gem 'friendly_id-globalize'

globalize migration:

class AddTranslationToPost < ActiveRecord::Migration[5.1]
  def up
  	Post.create_translation_table!({
  		title: :string,
      slug: :string,
  		sub_title: :string,
  		body: :text
  		}, {migrate_data: true})
  end

  def down
  	Post.drop_translation_table!
  end
end

my model:

class Post < ApplicationRecord
  translates :title,:slug ,:sub_title ,:body
  extend FriendlyId
  friendly_id :title, use: [:globalize, :history]
  has_attached_file :image, styles: { post_index: "750x300", large: "600x600", medium: "300x300>", thumb: "100x100#{}>" }
  validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/
 #def should_generate_new_friendly_id?
 #  title_changed?
 #end

end

show.html.erb

<%= link_to I18n.with_locale(:es){post_path(@post, locale: 'es')} do %>
  <%= image_tag('https://pilatesshopa.s3-eu-west-1.amazonaws.com/products/images/000/000/001/original/spain.png?1520005015', width: '23', height: '15.33', alt: 'English') %>
<% end %>
<%= link_to I18n.with_locale(:en){post_path(@post, locale: 'en')} do %>
  <%= image_tag('https://pilatesshopa.s3-eu-west-1.amazonaws.com/posts/images/000/000/001/original/usa.png?1520004588', width: '23', height: '15.33', alt: 'English') %>
<% end %>

friendly_id-globalize:

class AddLocaleToFriendlyIdSlugs < ActiveRecord::Migration[5.1]
  def change
    add_column :friendly_id_slugs, :locale, :string, length: 2, null: :false, after: :scope

    remove_index :friendly_id_slugs, [:slug, :sluggable_type]
    add_index :friendly_id_slugs, [:slug, :sluggable_type, :locale], length: { slug: 140, sluggable_type: 50, locale: 2 }
    remove_index :friendly_id_slugs, [:slug, :sluggable_type, :scope]
    add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope, :locale], length: { slug: 70, sluggable_type: 50, scope: 70, locale: 2 }, unique: true, name: :index_friendly_id_slugs_uniqueness
    add_index :friendly_id_slugs, :locale
  end
end

in Heroku everything is mixed up, I reset the database and it did not help.
thanks :)

Add to README - Config order is important

I've just tried to use friendly_id-globalize with globalize 4.0.3 (rails-4-2 branch) and got an error because FriendlyId config was called before translates. Even running something as simple as BlogPost.first raised an error

[1] pry(main)> BlogPost.first
NoMethodError: undefined method `translated_attribute_names' for BlogPost (call 'BlogPost.connection' to establish a connection):Class
from /Users/lenart/.rbenv/versions/2.2.0/lib/ruby/gems/2.2.0/gems/activerecord-4.2.0/lib/active_record/dynamic_matchers.rb:26:in `method_missing'

Adding this to the readme might save someone else the frustration of figuring this out on their own.

If it's easier for you I can prepare the pull request.

Scope is not applied for new records when history module is enabled

For globalized slugs, when both modules are enabled, scope and history, scope is not applied for new records, only for updated ones.

You can reproduce it creating two records with the same values excepting scoped data. Slug should be the same for both, but it doesn't. To let the same value, you have to modify translated slug with the same value of first record.

I think that the problem could be in the scope_for_slug_generator method of the history module version provided for this gem. Only when record exists the scope is used to get the slug table relationship.

Changing the order of friendly_id_config.uses?(:scoped) condition all works fine and slug is generated correctly:

@@ -109,12 +109,11 @@ method.
     # used slug.
     def scope_for_slug_generator
       relation = super
-      return relation if new_record?
-      relation = relation.merge(Slug.where('sluggable_id <> ?', id))
       if friendly_id_config.uses?(:scoped)
         relation = relation.where(Slug.arel_table[:scope].eq(serialized_scope))
       end
-      relation
+      relation = relation.merge(Slug.where('sluggable_id <> ?', id)) unless new_record?
+      return relation
     end

Create RubyGems release for 1.0.0.alpha4

Hello!

This issue is just a request for an update to the gem distributed at RubyGems.

In my project I use friendly_id-globalize at 1.0.0.alpha3. After upgrading to Rails 5.2 I am getting many of the following warning:

DEPRECATION WARNING: Dangerous query method (method whose arguments 
  are used as raw SQL) called with non-attribute argument(s): 
  "\"friendly_id_slugs\".id DESC".  

  Non-attribute arguments will be disallowed in Rails 6.0. This method should
  not be called with user-provided values, such as request parameters or model
  attributes. Known-safe values can be passed by wrapping them in Arel.sql(). 

I see this issue was already fixed in #26.

I'm happy to explicitly install the gem using the master ref instead of the RubyGems version in the meantime.

Thanks for your work on this gem and globalize!

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.